import { defineComponent, PropType, reactive, Ref, ref } from "vue";

export interface CanvasOption {
  isFullScreen?: boolean; //是否全屏手写 [Boolean] 可选
  isFullCover?: boolean; //是否全屏模式下覆盖所有的元素 [Boolean] 可选
  isDpr?: boolean; //是否使用dpr兼容高分屏 [Boolean] 可选
  lastWriteSpeed?: number; //书写速度 [Number] 可选
  lastWriteWidth?: number; //下笔的宽度 [Number] 可选
  lineCap?: "butt" | "round" | "square"; //线条的边缘类型 [butt]平直的边缘 [round]圆形线帽 [square]	正方形线帽
  lineJoin?: "round" | "bevel" | "miter"; //线条交汇时边角的类型  [bevel]创建斜角 [round]创建圆角 [miter]创建尖角。
  canvasWidth?: number; //canvas宽高 [Number] 可选
  canvasHeight?: number; //高度  [Number] 可选
  isShowBorder?: boolean; //是否显示边框 [可选]
  bgColor?: string; //背景色 [String] 可选 注:这背景色仅仅只是canvas背景,保存的图片仍然是透明的
  borderWidth?: number; // 网格线宽度  [Number] 可选
  borderColor?: string; //网格颜色  [String] 可选
  writeWidth?: number; //基础轨迹宽度  [Number] 可选
  maxWriteWidth?: number; // 写字模式最大线宽  [Number] 可选
  minWriteWidth?: number; // 写字模式最小线宽  [Number] 可选
  writeColor?: string | CanvasGradient | CanvasPattern; // 轨迹颜色  [String] 可选
  isSign?: boolean; //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
  imgType?: "png" | "jpeg"; //下载的图片格式  [String] 可选为 jpeg  canvas本是透明背景的
  lastWriteTime?: number;
  isWrite?: boolean;
  lastPoint?: Point;
  emptyCanvas?: string;
}

interface Point {
  x: number;
  y: number;
}

const defaultConfig: CanvasOption = {
  isFullScreen: false,
  isFullCover: false,
  isDpr: false,
  lastWriteSpeed: 1,
  lastWriteWidth: 2,
  lineCap: "round",
  lineJoin: "round",
  canvasWidth: 600,
  canvasHeight: 300,
  isShowBorder: true,
  bgColor: "#fefefe",
  borderWidth: 1,
  borderColor: "#aaa",
  writeWidth: 5,
  maxWriteWidth: 30,
  minWriteWidth: 5,
  writeColor: "#101010",
  isSign: true,
  imgType: "png",
  lastWriteTime: new Date().getTime(),
};

export default defineComponent({
  name: "TbSign",
  emits: ["confirm"],
  setup() {
    const canvas: Ref<HTMLCanvasElement | null> = ref(null);
    const config: CanvasOption = reactive({});
    const dpr = ref<number>(1);
    const context = ref<CanvasRenderingContext2D | null | undefined>(null);
    const resizeTimer = ref<number | null>(null);
    return {
      canvas,
      config,
      dpr,
      context,
      resizeTimer,
    };
  },

  props: {
    image: {
      required: false,
      type: String,
      default: null,
    },
    options: {
      required: false,
      type: Object as PropType<CanvasOption>,
      default: () => null,
    },
  },
  mounted() {
    this.init();
    //监听窗口变化
    window.addEventListener("resize", () => {
      if (this.resizeTimer) clearTimeout(this.resizeTimer);
      this.resizeTimer = setTimeout(() => {
        this.init();
      }, 100);
    });
  },
  watch: {
    options: {
      handler() {
        this.init();
      },
      deep: true,
    },
  },
  methods: {
    init() {
      this.config = Object.assign({}, defaultConfig, this.options);
      this.dpr = this.config.isDpr ? window.devicePixelRatio || 1 : this.dpr;
      if (this.canvas) {
        this.canvas.style.background = this.config.bgColor!;
      }
      this.context = this.canvas?.getContext("2d");
      if (this.config.isFullScreen) {
        this.config.canvasWidth =
          window.innerWidth || document.body.clientWidth;
        this.config.canvasHeight =
          window.innerHeight || document.body.clientHeight;
        if (this.config.isFullCover) {
          if (this.canvas) {
            this.canvas.style.position = "fixed";
            this.canvas.style.top = "0px";
            this.canvas.style.left = "0px";
            this.canvas.style.margin = "0px";
            this.canvas.style.zIndex = "20001";
          }
        }
      }
      if (this.canvas) {
        this.canvas.height = this.config.canvasHeight!;
        this.canvas.width = this.config.canvasWidth!;
      }
      this.canvasInit();
      this.canvasClear();
    },
    settingStyle(styles: CanvasOption) {
      if (this.canvas) {
        this.canvas.style.background = styles.bgColor!;
        this.canvas.style.position = "fixed";
      }
    },
    /**
     * 轨迹宽度
     */
    setLineWidth() {
      const nowTime = new Date().getTime();
      const diffTime = nowTime - this.config.lastWriteTime!;
      this.config.lastWriteTime = nowTime;
      let returnNum =
        this.config.minWriteWidth! +
        ((this.config.maxWriteWidth! - this.config.minWriteWidth!) * diffTime) /
          30;
      if (returnNum < this.config.minWriteWidth!) {
        returnNum = this.config.minWriteWidth!;
      } else if (returnNum > this.config.maxWriteWidth!) {
        returnNum = this.config.maxWriteWidth!;
      }
      //returnNum = returnNum.toFixed(2)
      //写字模式和签名模式
      if (this.config.isSign) {
        this.context!.lineWidth = this.config.writeWidth! * this.dpr;
      } else {
        const lineWidth = (this.config.lastWriteWidth =
          (this.config.lastWriteWidth! / 4) * 3 + returnNum / 4);
        this.context!.lineWidth = lineWidth * this.dpr;
      }
    },
    /**
     * 写开始
     */
    writeBegin(point: Point) {
      this.config.isWrite = true;
      this.config.lastWriteTime = new Date().getTime();
      this.config.lastPoint = point;
      this.writeContextStyle();
    },
    /**
     * 写结束
     */
    writeEnd(point: Point) {
      this.config.isWrite = false;
      this.config.lastPoint = point;
      this.saveAsImg();
    },
    /**
     * 绘制轨迹
     */
    writing(point: Point) {
      if (this.context) {
        this.context.beginPath();
        this.context.moveTo(
          this.config.lastPoint!.x * this.dpr,
          this.config.lastPoint!.y * this.dpr
        );
        this.context.lineTo(point.x * this.dpr, point.y * this.dpr);
        this.setLineWidth();
        this.context.stroke();
        this.config.lastPoint = point;
        this.context.closePath();
      }
    },
    /**
     * 轨迹样式
     */
    writeContextStyle() {
      if (this.context) {
        this.context.beginPath();
        this.context.beginPath();
        this.context.strokeStyle = this.config.writeColor!;
        this.context.lineCap = this.config.lineCap!;
        this.context!.lineJoin = this.config.lineJoin!;
      }
    },
    /**
     * 清空画板
     */
    canvasClear() {
      if (!this.context) return;
      if (!this.canvas) return;
      this.context.save();
      this.context.strokeStyle = this.config.writeColor!;
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.context.beginPath();
      this.context.lineWidth = this.config.borderWidth! * this.dpr;
      this.context.strokeStyle = this.config.borderColor!;
      const size = (this.config.borderWidth! / 2) * this.dpr;
      if (this.config.isShowBorder) {
        //画外面的框
        this.context.moveTo(size, size);
        this.context.lineTo(this.canvas.width - size, size);
        this.context.lineTo(
          this.canvas.width - size,
          this.canvas.height - size
        );
        this.context.lineTo(size, this.canvas.height - size);
        this.context.closePath();
        this.context.stroke();
      }
      if (this.config.isShowBorder && !this.config.isSign) {
        //画里面的框
        this.context.moveTo(0, 0);
        this.context.lineTo(this.canvas.width, this.canvas.height);
        this.context.lineTo(this.canvas.width, this.canvas.height / 2);
        this.context.lineTo(0, this.canvas.height / 2);
        this.context.lineTo(0, this.canvas.height);
        this.context.lineTo(this.canvas.width, 0);
        this.context.lineTo(this.canvas.width / 2, 0);
        this.context.lineTo(this.canvas.width / 2, this.canvas.height);
        this.context.stroke();
      }
      this.$emit("confirm", null);
      this.context.restore();
    },
    /**
     *  保存图片 格式base64
     */
    saveAsImg() {
      const image = new Image();
      image.src = this.canvas!.toDataURL(`image/${this.config.imgType}`);
      this.$emit("confirm", image.src);
      return image.src;
    },
    /**
     * 初始化画板
     */
    canvasInit() {
      if (!this.canvas) return;
      this.canvas.width = this.config.canvasWidth! * this.dpr;
      this.canvas.height = this.config.canvasHeight! * this.dpr;
      this.canvas.style.width = `${this.config.canvasWidth}px`;
      this.canvas.style.height = `${this.config.canvasHeight}px`;
      this.config.emptyCanvas = this.canvas.toDataURL(
        `image/${this.config.imgType}`
      );
    },
    /**
     * 鼠标按下 => 下笔
     */
    handleMousedown(e: MouseEvent) {
      this.writeBegin({ x: e.offsetX || e.clientX, y: e.offsetY || e.clientY });
    },
    /**
     * 书写过程 => 下笔书写
     */
    handleMousemove(e: MouseEvent) {
      this.config.isWrite && this.writing({ x: e.offsetX, y: e.offsetY });
    },
    /**
     * 鼠标松开 => 提笔
     */
    handleMouseup(e: MouseEvent) {
      this.writeEnd({ x: e.offsetX, y: e.offsetY });
    },
    /**
     * 离开书写区域 => 提笔离开
     */
    handleMouseleave(e: MouseEvent) {
      this.config.isWrite = false;
      this.config.lastPoint = { x: e.offsetX, y: e.offsetY };
    },
    /* ==========================移动端兼容=Start================================ */
    /**
     * 手指按下 => 下笔
     */
    handleTouchstart(e: TouchEvent) {
      const touch = e.targetTouches[0];
      const x = touch.clientX
        ? touch.clientX - this.getRect().left
        : touch.pageX - this.offset(touch.target, "left");
      const y = touch.clientY
        ? touch.clientY - this.getRect().top
        : touch.pageY - this.offset(touch.target, "top");
      this.writeBegin({ x, y });
    },
    /**
     * 手指移动 => 下笔书写
     */
    handleTouchmove(e: TouchEvent) {
      const touch = e.targetTouches[0];
      const x = touch.clientX
        ? touch.clientX - this.getRect().left
        : touch.pageX - this.offset(touch.target, "left");
      const y = touch.clientY
        ? touch.clientY - this.getRect().top
        : touch.pageY - this.offset(touch.target, "top");
      this.config.isWrite && this.writing({ x, y });
    },
    /**
     * 手指移动结束 => 提笔离开
     */
    handleTouchend(e: TouchEvent) {
      const tcs = e.targetTouches;
      const ccs = e.changedTouches;
      const touch: Touch | 0 =
        (tcs && tcs.length && tcs[0]) || (ccs && ccs.length && ccs[0]);
      if (touch === 0) return;
      const x = touch.clientX
        ? touch.clientX - this.getRect().left
        : touch.pageX - this.offset(touch.target, "left");
      const y = touch.clientY
        ? touch.clientY - this.getRect().top
        : touch.pageY - this.offset(touch.target, "top");
      this.writeEnd({ x, y });
    },
    /* ==========================移动端兼容=End================================== */
    /**
     * 下载二维码到本地
     */
    downloadSignImg(name: string) {
      const dataURL = this.canvas!.toDataURL("image/png");
      this.saveFile(
        dataURL,
        name
          ? `${name}.${this.config.imgType}`
          : `${new Date().getTime()}.${this.config.imgType}`
      );
    },
    /**
     * 保存文件
     */
    saveFile(data: string, filename: string) {
      const saveLink = document.createElementNS(
        "http://www.w3.org/1999/xhtml",
        "a"
      ) as HTMLAnchorElement;
      saveLink.href = data;
      saveLink.download = filename;
      const event = document.createEvent("MouseEvents");
      event.initMouseEvent(
        "click",
        true,
        false,
        window,
        0,
        0,
        0,
        0,
        0,
        false,
        false,
        false,
        false,
        0,
        null
      );
      saveLink.dispatchEvent(event);
    },
    /**
     * 获取画布的元素的大小及其相对于视口的位置
     * @return {}
     */
    getRect() {
      return this.canvas!.getBoundingClientRect();
    },
    /**
     * 获取dom对象的偏移量 可以获取解决position定位的问题
     * @returns number
     */
    offset(obj: EventTarget, direction: string): number {
      //将top,left首字母大写,并拼接成offsetTop,offsetLeft
      const _obj = obj as HTMLElement;
      const offsetDir: string =
        "offset" + direction[0].toUpperCase() + direction.substring(1);

      const _offsetDir = offsetDir as "offsetTop" | "offsetLeft";

      let realNum = _obj[_offsetDir]!;
      let positionParent: Element | null = _obj.offsetParent; //获取上一级定位元素对象
      while (positionParent != null) {
        const _positionParent = positionParent as HTMLElement;
        const _realNum = _positionParent[_offsetDir];
        realNum += _realNum;
        positionParent = _positionParent.offsetParent;
      }
      return realNum;
    },
    /**
     * 图片压缩
     */
    dealImage() {
      //压缩系数0-1之间
      const quality = 0.6;
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      const dealWidth = 300; //目标尺寸
      const dealHeight = 200;
      canvas.width = dealWidth;
      canvas.width = dealHeight;
      // if (Math.max(imgWidth, imgHeight) > w) {
      //     if (imgWidth > imgHeight) {
      //         canvas.width = w
      //         canvas.height = w * imgHeight / imgWidth
      //     } else {
      //         canvas.height = w
      //         canvas.width = w * imgWidth / imgHeight
      //     }
      // } else {
      //     canvas.width = imgWidth
      //     canvas.height = imgHeight
      //     quality = 0.6
      // }

      //const dataURL = this.canvas!.toDataURL("image/png")
      ctx!.clearRect(0, 0, canvas.width, canvas.height);
      ctx!.drawImage(this.canvas!, 0, 0, canvas.width, canvas.height);
      const ba = canvas.toDataURL("image/jpeg", quality); //压缩语句
      console.log(ba);
    },
  },

  render() {
    return (
      <canvas
        ref="canvas"
        class=""
        onMousedown={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleMousedown(e);
        }}
        onMousemove={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleMousemove(e);
        }}
        onMouseup={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleMouseup(e);
        }}
        onMouseleave={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleMouseleave(e);
        }}
        onTouchstart={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleTouchstart(e);
        }}
        onTouchmove={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleTouchmove(e);
        }}
        onTouchend={(e) => {
          e.preventDefault();
          e.stopPropagation();
          this.handleTouchend(e);
        }}
      >
        您的浏览器不支持canvas技术, 请升级浏览器!
      </canvas>
    );
  },
});
